package genedit.controller;

import genedit.model.graph.BreakPoint;
import genedit.model.graph.ConnectionPoint;
import genedit.model.graph.GraphElement;
import genedit.model.graph.Link;
import genedit.view.graph.ElementPainter;

import java.awt.Dimension;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class AdjustCommand extends Command {

	private int type;
	private List<GraphElement> elements;
	private List<Dimension> oldDimensions;
	private HashMap<ConnectionPoint, Point2D> connectionPointsBackup;
	private HashMap<Link, List<BreakPoint>> breakPointsBackup;
	private boolean useBackup;
	private Dimension avgAdjustment;

	public AdjustCommand(boolean isUndoable, int type) {
		super(isUndoable);
		this.setType(type);
		this.avgAdjustment = new Dimension(0, 0);
		this.oldDimensions = new ArrayList<Dimension>();
		this.useBackup=false;
	}

	@Override
	public void execute() {
		Iterator<GraphElement> it = elements.iterator();
		if (!useBackup) {
			//backup connectionPoints positions
			connectionPointsBackup=new HashMap<ConnectionPoint, Point2D>();
			for(GraphElement element:elements){
				for(ConnectionPoint point:element.getConnectionPoints()){
					connectionPointsBackup.put(point, new Point2D.Double(point.getPosition().getX(),point.getPosition().getY()));
				}
			}
			//beackup breakPoints
			breakPointsBackup=new HashMap<Link, List<BreakPoint>> ();
			for(GraphElement element:elements){
				for(ConnectionPoint point:element.getConnectionPoints()){
					Set<Link> links=new HashSet<Link>();
					links.addAll(point.getStarting());
					links.addAll(point.getEnding());
					
					for(Link link:links){
						List<BreakPoint> backupBreaks=new ArrayList<BreakPoint>();
						for(BreakPoint breakPoint:link.getBreakPoints()){
							Point2D bPoint=breakPoint.getPosition();
							backupBreaks.add(new BreakPoint(new Point2D.Double(bPoint.getX(),bPoint.getY())));
						}
						breakPointsBackup.put(link, backupBreaks);
					}
				}
			}
			
			// Ovaj deo racuna prosecnu sirinu i/ili visinu selektovanih
			// elemenata.
			while (it.hasNext()) {
				GraphElement element = it.next();
				if (element instanceof Link) {
					it.remove();
					continue;
				}

				oldDimensions.add(element.getSize());

				switch (type) {
				case 1:
					avgAdjustment.width += element.getSize().width;
					break;
				case 2:
					avgAdjustment.height += element.getSize().height;
					break;
				case 3:
					avgAdjustment.height += element.getSize().height;
					avgAdjustment.width += element.getSize().width;
					break;
				}
			}
			avgAdjustment.height /= elements.size();
			avgAdjustment.width /= elements.size();
			it = elements.iterator();
		}

		Dimension temp;

		while (it.hasNext()) {
			GraphElement element = it.next();

			if (avgAdjustment.getHeight() == 0)
				temp = new Dimension(avgAdjustment.width, element.getSize().height);
			else if (avgAdjustment.getWidth() == 0)
				temp = new Dimension(element.getSize().width, avgAdjustment.height);
			else
				temp = new Dimension(avgAdjustment);

			element.setSize(temp);
			getDocument().getPainter(element).resizeShape();
		}
		
		//vrati connectionPointe ako su izasli
		for(GraphElement element:elements){
			ElementPainter elementPainter=getDocument().getPainter(element);
			for(ConnectionPoint connectionPoint:element.getConnectionPoints()){
				if(!elementPainter.isElementAt(connectionPoint.getPosition())){
					Point2D newPoint=new Point2D.Double(element.getPosition().getX()+element.getSize().getWidth()/2,
							element.getPosition().getY()+element.getSize().getHeight()/2);
					connectionPoint.setPosition(newPoint);
				}
			}
		}
		
		for(GraphElement element:elements){
			for(ConnectionPoint point:element.getConnectionPoints()){
				Set<Link> links=new HashSet<Link>();
				links.addAll(point.getStarting());
				links.addAll(point.getEnding());
				for(Link link:links){
					link.removeIntersectPoints();
					link.rectLink();
					link.addIntersectionPoints();
				}
			}
		}

		getDocument().getModel().refresh();
	}

	@Override
	public void undo() {
		useBackup=true;
		int n = oldDimensions.size();

		for (int i = 0; i < n; i++) {
			elements.get(i).setSize(oldDimensions.get(i));
			getDocument().getPainter(elements.get(i)).resizeShape();
		}
		
		for(GraphElement element:elements){
			for(ConnectionPoint point:element.getConnectionPoints()){
				point.setPosition(connectionPointsBackup.get(point));
			}
		}
		//beackup breakPoints
		for(GraphElement element:elements){
			for(ConnectionPoint point:element.getConnectionPoints()){
				Set<Link> links=new HashSet<Link>();
				links.addAll(point.getStarting());
				links.addAll(point.getEnding());
				
				for(Link link:links){
					link.setBreakPoints(breakPointsBackup.get(link));
				}
			}
		}

		getDocument().getModel().refresh();
	}

	public List<GraphElement> getElements() {
		return elements;
	}

	public void setElements(List<GraphElement> elements) {
		this.elements = new ArrayList<GraphElement>();
		for (Iterator<GraphElement> it = elements.iterator(); it.hasNext();)
			this.elements.add(it.next());
	}

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

	public Dimension getAvgAdjustment() {
		return avgAdjustment;
	}

	public void setAvgAdjustment(Dimension avgAdjustment) {
		this.avgAdjustment = avgAdjustment;
	}

	public List<Dimension> getOldDimensions() {
		return oldDimensions;
	}

	public void setOldDimensions(List<Dimension> oldDimensions) {
		this.oldDimensions = oldDimensions;
	}

}
